package com.yl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yl.common.SnowflakeIdWorker;
import com.yl.exception.CustomException;
import com.yl.mapper.MenuMapper;
import com.yl.mapper.UserMapper;
import com.yl.model.dto.MenuDTO;
import com.yl.model.entity.MenuEntity;
import com.yl.model.entity.MenuEntity;
import com.yl.model.entity.UserEntity;
import com.yl.model.vo.MenuTreeVO;
import com.yl.model.vo.MenuVO;
import com.yl.service.MenuService;
import com.yl.service.UserService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;


@Service
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
public class MenuServiceImpl extends ServiceImpl<MenuMapper, MenuEntity> implements MenuService {


    @Resource
    private UserService sysUserService;

    @Resource
    private UserMapper sysUserMapper;

    @Resource
    private MenuMapper menuMapper;

    @Resource
    private SnowflakeIdWorker idWorker;

    @Override
    public List<MenuTreeVO> getCurrentUserNav() {
        String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        UserEntity sysUser = sysUserService.getByUsername(username);

        List<Long> menuIds = sysUserMapper.getNavMenuIds(sysUser.getId());

        List<MenuVO> menus = this.listByIds(menuIds).stream()
                .map(e -> BeanUtil.copyProperties(e, MenuVO.class))
                .collect(Collectors.toList());
        // 转树状结构
        List<MenuVO> menuTree = buildTreeMenu(menus);

        // 实体转DTO
        return convert(menuTree);
    }

    @Override
    public List<Tree<String>> tree() {

        //方式一
        // 获取所有菜单信息
//        List<Menu> sysMenus = this.list(new QueryWrapper<Menu>().orderByAsc("orderNum"));
        // 转成树状结构
        //		return buildTreeMenu(sysMenus);


        //方式二
        //利用mysql
        List<MenuVO> treeList = menuMapper.tree();        //mysql递归查询语法
        //todo ID转Strung,应为Long过长类型传前端精度丢失
        //用hutool工具包转换成数据结构的新方式
        List<Tree<String>> build = TreeUtil.build(treeList, "0", new TreeNodeConfig(),
                (menu, treeNode) -> {
                    treeNode.setId(menu.getId()+"");
                    treeNode.setParentId(menu.getParentId()+"");
                    treeNode.setName(String.valueOf(menu.getName()));
                    treeNode.put("path", menu.getPath());
                    treeNode.put("icon", menu.getIcon());
                    treeNode.put("component", menu.getComponent());
                    treeNode.put("perms", menu.getPerms());
                    treeNode.put("type", menu.getType());
                    treeNode.put("orderNum", menu.getOrderNum());
                    treeNode.put("children", menu.getChildren());
                });


        return build;

    }

    @Override
    public MenuEntity saveObj(MenuEntity entity) {
        entity.setId(idWorker.nextId());
        entity.setCreateTime(new Date());
        entity.setIsDel(false);
        boolean save = this.save(entity);
        if (!save) throw new CustomException("保存数据失败");
        return entity;
    }

    @Override
    public MenuVO updateObj(MenuDTO dto) {
        MenuEntity menu = BeanUtil.copyProperties(dto, MenuEntity.class);
        menu.setUpdateTime(new Date());
        boolean b = this.updateById(menu);
        if (!b) throw new CustomException("修改数据失败");

        // 清除所有与该菜单相关的权限缓存
        sysUserService.clearUserAuthorityInfoByMenuId(menu.getId());
        return BeanUtil.copyProperties(menu, MenuVO.class);
    }

    @Override
    public Boolean del(Long id) {

        int count = this.count(new QueryWrapper<MenuEntity>().eq("parent_id", id));
        if (count > 0) {
            throw new CustomException("请先删除子菜单");
        }

        // 清除所有与该菜单相关的权限缓存
        sysUserService.clearUserAuthorityInfoByMenuId(id);
        int del = menuMapper.del(id);
        if(del==0) throw new CustomException("删除菜单数据失败");
        return true;
    }

    private List<MenuTreeVO> convert(List<MenuVO> menuTree) {
        List<MenuTreeVO> menuDtos = new ArrayList<>();

        menuTree.forEach(m -> {
            MenuTreeVO dto = new MenuTreeVO();

            dto.setId(m.getId());
            dto.setName(m.getPerms());
            dto.setTitle(m.getName());
            dto.setComponent(m.getComponent());
            dto.setPath(m.getPath());

            if (m.getChildren().size() > 0) {

                // 子节点调用当前方法进行再次转换
                dto.setChildren(convert(m.getChildren()));
            }

            menuDtos.add(dto);
        });

        return menuDtos;
    }

    private List<MenuVO> buildTreeMenu(List<MenuVO> menus) {

        List<MenuVO> finalMenus = new ArrayList<>();
        // 先各自寻找到各自的孩子

        for (MenuVO menu : menus) {

            for (MenuVO e : menus) {
                if (menu.getId() == e.getParentId()) {
                    menu.getChildren().add(e);
                }
            }
            // 提取出父节点
            if (menu.getParentId() == 0L) {
                finalMenus.add(menu);
            }
        }
        return finalMenus;
    }
}
